/******************************************************************************
 * Copyright 2007 - 2021 Realtek Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/
 
#ifndef __RTW_INTF_C__
#define __RTW_INTF_C__

#include "drv_types.h"
#include "os_intfs.h"
#include "rtw_intf.h"
#include "rtw_wlan_config.h"

bool b_wifi_open = _FALSE;
extern struct net_device*	rtw_netdev[NET_DEV_NUM];
extern Rltk_wlan_t rltk_wlan_info[NET_DEV_NUM];
struct netif rtw_netif[NET_DEV_NUM];
#define MAX_WAIT_PROBE_SUCCESS_TIME   300//50
#define MAX_WPS_TIME   1500
#define MAX_CONNECT_TIME   100//600

void (*promisc_callback_export)(unsigned char*, unsigned int) = NULL;
_mutex promisc_mutex;


int rltk_wlan_init(int idx_wlan);
void rltk_wlan_deinit(void);


/*****************************************************************************
function name: rtw_wifi_probe
Function:  register usb device to system
Parameter: None
*****************************************************************************/
int  rtw_wifi_probe(void)
{
	DBG_INFO("rtw_wifi_probe");
	rtw_drv_entry();
}

/*****************************************************************************
function name: rtw_wifi_check_probe_ok
Function:  check probe has been ready
Parameter: None
Return Value: 1: susccess
			0: Fail
*****************************************************************************/
unsigned int rtw_wifi_check_probe_ok()
{
	unsigned int i = 0;
	int ret = _SUCCESS;

	for(i=0;i<MAX_WAIT_PROBE_SUCCESS_TIME;i++)
	{
		if((rtw_netdev[0] != NULL) && (strcmp(rtw_netdev[0]->name,"\0")))
		{
			RTW_INFO("[rtw_wifi_check_probe_ok]  rtw_netdev[0].ifname: %s \n",rtw_netdev[0]->name);
			break;
		}
		else
		{
			rtw_msleep_os(3);
		}
	}
	if(i == MAX_WAIT_PROBE_SUCCESS_TIME)
	{
		RTW_INFO("Probe Failed \n");
		ret = _FAIL;
	}

	return ret;
}

int  rtw_drv_open(struct NetDevice *ndev,int mode)
{
	s8 idx;
	int status = -1;
	struct net_device *dptr;
	struct NetDevice *dev;
	struct NetDeviceInterFace *netDeviceIf;
	
	if(b_wifi_open==_TRUE)
	{
		RTW_INFO("Wifi has already open!");
		return 0;
	}

	for(idx=NET_DEV_NUM - 1;idx>=0;idx--){

		rtw_netdev[idx]->dev = ndev;
	
		rtw_hook_if_ops(rtw_netdev[idx]);
		_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[idx]);
		//rtw_init_netdev_name(ndev, name);
		_rtw_memcpy(rtw_netdev[idx]->name, ndev->name, 16);
		_rtw_memcpy(ndev->macAddr, adapter_mac_addr(padapter), ETH_ALEN);
		//_rtw_memcpy(rtw_netdev[idx]->dev_addr, adapter_mac_addr(padapter), ETH_ALEN);
		
		//padapter->registered = 1;	
		netDeviceIf = ndev->netDeviceIf;

		//dptr = rtw_netdev[idx];
		//dev = dptr->dev;
		//netDeviceIf = dev->netDeviceIf;
		
		if(!netDeviceIf->open || (status = netDeviceIf->open(rtw_netdev[idx]) != 0)){
			RTW_INFO("open wifi device failed!");
			return status;
		}
		if(mode == WC_FLAG_MODE_INFRA)
			NetDeviceAdd(rtw_netdev[idx]->dev,2);
		else if(mode == WC_FLAG_MODE_MASTER)
			NetDeviceAdd(rtw_netdev[idx]->dev,3);
	}
	b_wifi_open = _TRUE;
	return 0;
}

/*****************************************************************************
function name: rtw_wifi_driver_version
Function:  get wifi driver version
Parameter: none
*****************************************************************************/
void rtw_wifi_driver_version()
{
	printk("RealTek wifi driver version : %s\n", DRIVERVERSION);
}

/*****************************************************************************
function name: rtw_wifi_close
Function:  close wifi adapter
Parameter: None
*****************************************************************************/
int  rtw_wifi_close(void)
{
	struct net_device *dptr = rtw_netdev[0];
	struct NetDevice *dev = dptr->dev;
	struct NetDeviceInterFace *netDeviceIf=dev->netDeviceIf;
	s8 idx;
	int ret = -1;

	if(b_wifi_open==_FALSE)
	{
		RTW_INFO("Wifi has already closed!");
		return 0;
	}

	
	for(idx = 0;idx < NET_DEV_NUM;idx++)
	{
		dptr = rtw_netdev[idx];
		dev = dptr->dev;
		DestroyEapolData(dev);
				
		netDeviceIf = dev->netDeviceIf;
				
		ret = netDeviceIf->stop(dev);
		if(ret != 0) {
			RTW_INFO("stop device failed,ret:%d \n",ret);
			return ret;
		}

		NetDeviceDelete(dev);
	}
	b_wifi_open = _FALSE;

	return 0;
}
int rtw_wifi_isdisconnected()
{
	int status=0,tc=0;
    _adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;

	while(tc<50){
		rtw_msleep_os(100);
		if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _FALSE){	
			status=1;
			break;
		} 
		tc++;
	}
	return status;
}
/*****************************************************************************
function name: rtw_wifi_close
Function:  close wifi adapter
Parameter: isSecurity: security or not

*****************************************************************************/
int rtw_wifi_connect_ready()
{
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;
	s32 tc = 0;
	int Authentication = padapter->securitypriv.ndisauthtype;

	while(pmlmepriv->wpa_phase && tc<MAX_WPS_TIME)
	{
		rtw_msleep_os(100);
		tc++;
	}
	if(pmlmepriv->wpa_phase)
	{
		RTW_INFO("wps fail\n");
		pmlmepriv->wpa_phase = 0;
		return 0;
	}

	tc = 0;

	while(tc < MAX_CONNECT_TIME){
		rtw_msleep_os(100);
		if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) 
				break;	
		tc++;
	}

	if(tc == MAX_CONNECT_TIME)
	{
		RTW_DBG("connect fail.\n");
		return 0;
	}

	if(padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_Open)
	{
		return 1;
	}
	else //wpa
	{
		tc = 0;
		while(tc < MAX_CONNECT_TIME){
			rtw_msleep_os(100);
			if (padapter->securitypriv.binstallGrpkey == _TRUE) 
				break;
			tc++;
		}

		if(tc == MAX_CONNECT_TIME)
		{
			RTW_DBG("key set fail.\n");
			return 0;
		}
		else
		{
			return 1;
		}
	}			
}

int rtw_hapd_is_started()
{
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;
	s32 tc = 0;

	while(tc < 255){
		rtw_msleep_os(100);
		if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == _TRUE) 
				break;
			tc++;
	}

	if(tc == 255)
	{
		RTW_DBG("hostapd start fail.\n");
		return 0;
	}
	
	return 1;
}
void rtw_set_wps_enable(int wpa_phase)
{
	_adapter *padapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;

	pmlmepriv->wpa_phase = wpa_phase;	
}



/****************************************************************************************************
*func name: rtw_register_promisc_callback
*description: this function is used to allocate resourec  register function to get 802.11 pkt
****************************************************************************************************/
void rtw_register_promisc_callback(void (*callback)(unsigned char*, unsigned int))
{
	_rtw_mutex_init(&promisc_mutex);

	_enter_critical_mutex(&promisc_mutex,NULL);
	promisc_callback_export = callback;
	_exit_critical_mutex(&promisc_mutex,NULL);
}


/****************************************************************************************************
*func name: rtw_indicate_promisc_pkt
*description: this function is used to indicate packet of  wifi in promisc mode
****************************************************************************************************/
void rtw_indicate_promisc_pkt(unsigned char* buf, unsigned int len)
{
	if(promisc_callback_export)
	{
		_enter_critical_mutex(&promisc_mutex,NULL);
		promisc_callback_export(buf,len); 
		_exit_critical_mutex(&promisc_mutex,NULL);
	}
}

/****************************************************************************************************
*func name: rtw_unregister_promisc_callback
*description: this function is used to unregister function to get 802.11 pkt and free resource
****************************************************************************************************/
void rtw_unregister_promisc_callback(void)
{
	_enter_critical_mutex(&promisc_mutex,NULL);
	promisc_callback_export = NULL;
	_exit_critical_mutex(&promisc_mutex,NULL);
	
	_rtw_mutex_free(&promisc_mutex);
}



/****************************************************************************************************
*func name: rtw_promisc_mode_set
*description: promisc mode enable or not
****************************************************************************************************/
void rtw_promisc_mode_set(bool benable)
{
	if(benable)
	{
		rtw_wc_set_mode(rltk_wlan_info[0].dev, WC_FLAG_MODE_MONITOR);
	}
	else
	{
		rtw_wc_set_mode(rltk_wlan_info[0].dev, WC_FLAG_MODE_INFRA);
	}
}

/****************************************************************************************************
*func name: rtw_promisc_switch_channel
*description: switch channel interface
****************************************************************************************************/
void rtw_promisc_switch_channel(unsigned int channel)
{
	rtw_wc_set_freq(rltk_wlan_info[0].dev, 0, rtw_ch2freq(channel));
}


/*****************************************************************************************************
* func name: rtw_dump_llt_entry()
* description: this function is used to dump LLT enrtys to check whether the LLT linked correctly
* history: created based on RTL8188E by page, if the related registers changed on other chips, please 
*			note and clarify
* creator: wlan 1 page
* others:          
******************************************************************************************************/
void rtw_dump_llt_entry(void)
{
	_adapter *Adapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	u8	i;
	u16  page_num = 0xff; //RTL8188E has 0xff pages
		
	for (i = 0; i<page_num; i++) {
		rtw_write8(Adapter, 0x1e1, i);
		rtw_write8(Adapter, 0x1e3, 0xc0);
		dprintf("LLT: entry %x, LLT value 0x%x\n", i, rtw_read8(Adapter, 0x1e0));
			
	}
}

/*****************************************************************************************************
* func name: rtw_dump_txpkt_buf()
* description: this function is used to dump content of the tx pkt buffer to check whether the content 
*               is correctly
* history: created based on RTL8188E by page, if the related registers changed on other chips, please 
*			note and clarify
* creator: wlan 1 page
* others:  REG_PKT_BUFF_ACCESS_CTRL: 0x106
*          REG_PKTBUF_DBG_CTRL: 0x140
*		   REG_PKTBUF_DBG_DATA_L: 0x144
*		   REG_PKTBUF_DBG_DATA_H: 0x148        
******************************************************************************************************/
void rtw_dump_txpkt_buf(void)
{
	_adapter *Adapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	u32 dword = 0;
	u32 dword1 = 0;
	u32 dword2 = 0;
	u32 page_idx = 0;	// there're 256 pages in 88EM
	u32 page_offset = 0;	// each page has 128 bytes
	u32 count = 0;
		
				
	//switch to txbuf
	rtw_write8(Adapter, REG_PKT_BUFF_ACCESS_CTRL, 0x69);

	for (page_idx = 0; page_idx < 0xFF; page_idx++) {
		//read unit 8 byte
		for (page_offset = page_idx * 128; page_offset < page_idx*128+128; page_offset += 8) {
			//write addr entry & BIT(24) = 0
			rtw_write32(Adapter, REG_PKTBUF_DBG_CTRL, (page_offset/8));

			//polling ready BIT(24) = 1
			while (1) {
				count++;
				dword = rtw_read32(Adapter, REG_PKTBUF_DBG_CTRL);
#ifdef CONFIG_RTL8188F
				if ((dword & BIT(23))) 
#else
				if ((dword & BIT(24))) 
#endif
				{
					break;
				} else {
					if (count == 100) {
						dprintf("TXPKT BUF: polling 0x140 BIT(24) fail");
						return;
					}
				}
			}

			//read 8 byte
			dword1 = rtw_read32(Adapter, REG_PKTBUF_DBG_DATA_L);
			dword2 = rtw_read32(Adapter, REG_PKTBUF_DBG_DATA_H);
			dprintf("TXPKT BUF: page 0x%x, addr_entry:%08x dword1:%08x, dword2:%08x\n",
				page_idx, (page_offset/8), dword1, dword2);

		}
	}
}

void rtw_dump_rxpkt_buf(void)
{
	_adapter *Adapter = (_adapter *)rtw_netdev_priv(rtw_netdev[0]);
	u32 dword = 0;
	u32 dword1 = 0;
	u32 dword2 = 0;
	u32 page_idx = 0;	// there're 256 pages in 88EM
	u32 page_offset = 0;	// each page has 128 bytes
	u32 count = 0;
		
				
	//switch to rxbuf
	rtw_write8(Adapter, REG_PKT_BUFF_ACCESS_CTRL, 0xa5);

	for (page_idx = 0; page_idx < 0xFF; page_idx++) {
		//read unit 8 byte
		for (page_offset = page_idx * 128; page_offset < page_idx*128+128; page_offset += 8) {
			//write addr entry & BIT(16) = 0
			rtw_write32(Adapter, REG_PKTBUF_DBG_CTRL, (page_offset/8));

			//polling ready BIT(16) = 1
			while (1) {
				count++;
				dword = rtw_read32(Adapter, REG_PKTBUF_DBG_CTRL);
				if ((dword & BIT(16))) {
					break;
				} else {
					if (count == 100) {
						dprintf("RXPKT BUF: polling 0x140 BIT(24) fail");
						return;
					}
				}
			}

			//read 8 byte
			dword1 = rtw_read32(Adapter, REG_PKTBUF_DBG_DATA_L);
			dword2 = rtw_read32(Adapter, REG_PKTBUF_DBG_DATA_H);
			dprintf("RXPKT BUF: page 0x%x, addr_entry:%08x dword1:%08x, dword2:%08x\n",
				page_idx, (page_offset/8), dword1, dword2);

		}
	}
}


u8 rtw_RateParseFunc(PADAPTER pAdapter, u8 *targetStr)
{
	u16 i=0;
 	u8* rateindex_Array[] = { "1M","2M","5.5M","11M","6M","9M","12M","18M","24M","36M","48M","54M",
 								"HTMCS0","HTMCS1","HTMCS2","HTMCS3","HTMCS4","HTMCS5","HTMCS6","HTMCS7",
 								"HTMCS8","HTMCS9","HTMCS10","HTMCS11","HTMCS12","HTMCS13","HTMCS14","HTMCS15",
 								"HTMCS16","HTMCS17","HTMCS18","HTMCS19","HTMCS20","HTMCS21","HTMCS22","HTMCS23",
 								"HTMCS24","HTMCS25","HTMCS26","HTMCS27","HTMCS28","HTMCS29","HTMCS30","HTMCS31",
 								"VHT1MCS0","VHT1MCS1","VHT1MCS2","VHT1MCS3","VHT1MCS4","VHT1MCS5","VHT1MCS6","VHT1MCS7","VHT1MCS8","VHT1MCS9",
 								"VHT2MCS0","VHT2MCS1","VHT2MCS2","VHT2MCS3","VHT2MCS4","VHT2MCS5","VHT2MCS6","VHT2MCS7","VHT2MCS8","VHT2MCS9",
 								"VHT3MCS0","VHT3MCS1","VHT3MCS2","VHT3MCS3","VHT3MCS4","VHT3MCS5","VHT3MCS6","VHT3MCS7","VHT3MCS8","VHT3MCS9",
 								"VHT4MCS0","VHT4MCS1","VHT4MCS2","VHT4MCS3","VHT4MCS4","VHT4MCS5","VHT4MCS6","VHT4MCS7","VHT4MCS8","VHT4MCS9"};

	for(i=0;i<=83;i++){	
		if(strcmp(targetStr, rateindex_Array[i]) == 0){
			RTW_DBG("%s , index = %d \n",__func__ ,i);
			return i+1;
		}
	}
	
	printk("%s ,please input a Data RATE String as:",__func__);
	for(i=0;i<=83;i++){	
		printk("%s ",rateindex_Array[i]);
		if(i%10==0)
			printk("\n");
	}	
	return _FAIL;
}

#endif
